module;
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>

export module Code_2_2_4;

class person_t;


typedef std::pair<std::string, bool> list_item;

std::string title(const list_item& item)
{
    return item.first + std::string(item.second, '*');
}

bool is_selected(const list_item& item)
{
    return item.second;
}

bool is_not_selected(const list_item& item)
{
    return !item.second;
}

// This function groups the selected items in a collection
// and moves them to the desired location
// (see section 2.2.4 and figure 2.11)
template <typename It>
void move_selected_to(It first, It last, It destination)
{
    std::stable_partition(first, destination, is_not_selected);
    std::stable_partition(destination, last, is_selected);
}


export
namespace Code_2_2_4 {

    void run()
    {
        std::vector<list_item> people {
            { "David"  , true },
            { "Jane"   , false },
            { "Martha" , false },
            { "Peter"  , false },
            { "Rose"   , true },
            { "Tom"    , true }
        };

        move_selected_to(people.begin(), people.end(), people.begin() + 3);

        for (const auto& person: people) {
            std::cout << title(person) << '\n';
        }
    }
}

class person_t {
public:
    enum gender_t {
        female,
        male,
        other
    };

    enum output_format_t {
        name_only,
        full_name
    };

    person_t()
        : m_name("John")
        , m_surname("Doe")
        , m_gender(other)
    {
    }

    person_t(std::string name, gender_t gender, int age = 0)
        : m_name(name)
        , m_surname("Doe")
        , m_gender(gender)
        , m_age(age)
    {
    }

    person_t(std::string name, const std::string& surname, gender_t gender, int age = 0)
        : m_name(name)
        , m_surname(surname)
        , m_gender(gender)
        , m_age(age)
    {
    }

    std::string name() const
    {
        return m_name;
    }

    std::string surname() const
    {
        return m_surname;
    }

    gender_t gender() const
    {
        return m_gender;
    }

    int age() const
    {
        return m_age;
    }

    void print(std::ostream& out,
        person_t::output_format_t format) const
    {
        if (format == person_t::name_only) {
            out << name() << '\n';

        }
        else if (format == person_t::full_name) {
            out << name() << ' '
                << surname() << '\n';

        }
    }

private:
    std::string m_name;
    std::string m_surname;
    gender_t m_gender;
    int m_age;
};